#ifndef __GAME_HTTP_H__
#define __GAME_HTTP_H__

#include "game_def.h"
#include "game_socket.h"
#include "game_tl.h"
#include <string>
#include <vector>
#include <event2/http.h>
#include <event2/http_struct.h>
#include <map>


#ifndef CGAME_HTTPD_THREAD_NUM
#define CGAME_HTTPD_THREAD_NUM 1
#endif

#ifndef CGAME_HTTPD_HTML_TL
#define CGAME_HTTPD_HTML_TL "<html><head><title>%s</title></head><body><center><h1>%s</h1></center><hr><center>CGameHttpd/%s</center></body></html>"
#endif

#define CGAME_HTTPD_NAME        "CGameHttpd"



class CGameHttpd;

struct HttpdWorker_t {
  event_base *_evbase;
  evhttp  *_evhttp;
  thread_t _thread;
  CGameHttpd *_master;
};

typedef evhttp_request HttpdRequest_t;
typedef std::map<std::string,std::string> httpd_parameters_t;
typedef std::map<std::string,std::string> httpd_cookies_t;

namespace CGame{
namespace Httpd{

template <typename TYPE, void (TYPE::*onProcess)(HttpdRequest_t *request) >
void __httpd_evhttp_cb(HttpdRequest_t *request,void* arg)
{
  TYPE* This = (TYPE*)arg;
  This->onProcess(request);
}


template <typename TYPE, void (TYPE::*work)(HttpdWorker_t* ctx) >
void* _httpd_thread_cb(void* arg)//线程启动函数，声明为模板函数
{
  HttpdWorker_t* ctx = (HttpdWorker_t*)arg;
  TYPE* This = (TYPE*)ctx->_master;
  This->work(ctx);
  return NULL;
}

void __evhttpd_set_content_lenght(evkeyvalq *headers, size_t len);


typedef int CookieExpires_t;

struct Cookie_t{
  std::string _name;
  std::string _value;
  std::string _path;
  BOOL _httpOnly;
  int _expiry;
};


struct Status_t
{
  int code;
  const CHAR *message;
};

class Status
{
public:
  static Status_t BADREQUEST;
  static Status_t FORBIDDEN;
  static Status_t NOTFOUND;
  static Status_t BADMETHOD;
  static Status_t ENTITYTOOLARGE;
  static Status_t EXPECTATIONFAILED;
  static Status_t INTERNAL;
  static Status_t NOTIMPLEMENTED;
  static Status_t SERVUNAVAIL;
};

class MediaType
{
public:
  static const char* TEXT_CSS_VALUE;
  static const char* TEXT_CSS_UTF8_VALUE;
  static const char* APPLICATION_FORM_URLENCODED_VALUE;
  static const char* MULTIPART_FORM_DATA_VALUE;
  static const char* APPLICATION_JAVASCRIPT_VALUE;
  static const char* APPLICATION_JAVASCRIPT_UTF8_VALUE;
  static const char* APPLICATION_JSON_VALUE;
  static const char* APPLICATION_JSON_UTF8_VALUE;
  static const char* APPLICATION_OCTET_STREAM_VALUE;
  static const char* TEXT_HTML_UTF8_VALUE;
  static const char* IMAGE_JPEG_VALUE;
  static const char* IMAGE_PNG_VALUE;
  static const char* IMAGE_GIF_VALUE;
  static const char* TEXT_HTML_VALUE;
  static const char* TEXT_XML_VALUE;
  static const char* IMAGE_SVG_VALUE;
  static const char* FONT_WOFF2_VALUE;
  static const char* FONT_TTF_VALUE;

  static const char* getContentType(const char* ext);
};

class UserAgent{
public:
  static BOOL isChrome(const char* ua);
};

class Response;

class Request:public CGameObject{
public:
  enum class Method{
    GET,
    POST,
    HEAD,
    PUT,
    DELETE,
    OPTIONS
  };


private:
  Method _mothod;

  class Mark{
  public:
    static const int Parameter  = 0;
    static const int Cookie     = 1;
  };

  HttpdRequest_t *_request;
  httpd_parameters_t _params;
  httpd_cookies_t _cookies;
  int _mark = 0;
  void parameters();
  void cookies();
  void mark(int idx);
  BOOL isMark(int idx);
public:
  Request(HttpdRequest_t *request);
  virtual ~Request();
  Method getMethod();
  const char* getHeader(const char* name);
  INT32 getInt32Header(const char* name);
  const char* getQueryString() const;
  const char* getRequestURL();
  const char* getHost() const;
  size_t getContentLength() const;
  const char* getContetType() const;
  const char* getParameter(const char* name);
  const char* getCookie(const char* name);
  const httpd_parameters_t& getParameterMap();
  const char* getScheme() const;
  int getPort();
  HttpdRequest_t* getRequest();
private:
  static Method __evhttp_get_method(HttpdRequest_t *request);
  const evhttp_uri* _evhttp_uri;


  friend class Response;
  friend class CGameHttpd;
};

class Response:public CGameObject{
protected:
  CGameHttpd* _master;
  void html(Request* req,int status,const char* format,va_list args);
  void send(Request* req,int status);
public:
  Response(CGameHttpd* master);
  void stream(Request* req,int fd,const char* ext);

  void html(Request* req,const char* content);
  void html(Request* req,int status,const char* format,...);
  void redirect(Request* req,const char* dest);
  void redirect(Request* req,const char* dest,int code);
  void header(Request* req,const char* name,const char* val);
  void cookie(Request* req,const char* name,const char* val);
  void cookie(Request* req,const char* name,const char* val,CookieExpires_t expires);
};

class Controller:public CGameObject{
public:
  virtual BOOL init() = 0;
};

typedef CMemFun_t<Controller, void, Request*> Handler;
typedef std::map<std::string,Handler> HandlerMap;

};
};




class CGameHttpd:public CGameSocketd{
public:
  typedef UINT16 Methods_t;
  const static Methods_t GET;
  const static Methods_t POST;
private:
  int _numWorkers;
  std::vector<HttpdWorker_t*> _workersVec;
  int _port;
  std::string _addr;
  std::string _vstring;
  CGame::Httpd::HandlerMap _handlerMap;

  template <typename TYPE, void (TYPE::*onProcess)(HttpdRequest_t *request) >
  friend void CGame::Httpd::__httpd_evhttp_cb(HttpdRequest_t *request,void* arg);
  template <typename TYPE, void (TYPE::*work)(HttpdWorker_t* ctx) >
  friend void* CGame::Httpd::_httpd_thread_cb(void* arg);

protected:
  std::string _root;
  std::string _index;
  BOOL _stop = FALSE;
  CGame::Httpd::Response* _response;
  mseconds_t _sleep = 40;
  virtual const char* getName();


  void onProcess(HttpdRequest_t *request);
  virtual void doProcess(CGame::Httpd::Request* req);
  virtual void work(HttpdWorker_t* ctx);
  const char* getRoot() const;
  void error(CGame::Httpd::Request* req,int code);
  virtual BOOL tryHandle(CGame::Httpd::Request* req);


  static void getMappingString(char* dest, size_t dest_max, CGame::Httpd::Request::Method m,const char* path);

  void get(const char* c,CGame::Httpd::Handler handler);
  void post(const char* c,CGame::Httpd::Handler handler);
  void addRequestMapping(const char* value,CGame::Httpd::Handler handler);
  void addRequestMapping(Methods_t methods,const char* value,CGame::Httpd::Handler handler);


  virtual CGame::Httpd::Response* newResponse();
  virtual CGame::Httpd::Request* newRequest(HttpdRequest_t* request);
public:
  CGameHttpd();
  CGameHttpd(const char* root);
  CGameHttpd(const char* root,const char* index);



  virtual BOOL init(int port);
  BOOL init(int port,int numWorkers);
  BOOL init(const char* addr,int port);
  BOOL init(event_base* evbase,const char* addr,int port,int numWorkers);
  void run();
  BOOL stop();

  const std::string& vstring() const;


  // void post(const char* c);
};

#endif
